use hypers::prelude::*;
use serde::{Deserialize, Serialize};
use hypers::tracing::info;
use hypers::State;

#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
pub struct User {
    pub email: Option<String>,
    pub age: Option<u16>,
}

#[derive(Serialize, Deserialize)]
pub struct HeaderParams<'a> {
    pub host: Option<&'a str>,
    #[serde(rename(deserialize = "user-agent"))]
    pub user_agent: Option<&'a str>,
    pub accept: Option<&'a str>,
}

// http://127.0.0.1:7878/basic/header
// Request Header Params
pub async fn header(mut req: Request) -> Result<Response> {
    let state = req.state::<AppState>().unwrap();
    println!("state = {:?}",state);

    let headers = req.parse_header::<HeaderParams<'_>>()?;
    let mut res = Response::default();
    res.status(200).json(&headers);
    Ok(res)
}

// http://127.0.0.1:7878/basic/admin@outlook.com/18
// Uri Path Params
pub async fn delete(req: Request) -> impl Responder {
    let user = req.parse_param::<User>()?;
    Ok::<_, Error>((200, Json(user)))
}

// http://127.0.0.1:7878/basic/query?email=admin@outlook.com&age=19
// Query Params
pub async fn query(mut req: Request) -> impl Responder {
    let state = req.state::<AppState>().unwrap();
    println!("state = {:?}",state);
    let user = req.parse_query::<User>()?;
    Ok::<_, Error>((200, Json(user)))
}

// http://127.0.0.1:7878/basic/create
// Context-Type : application/json
pub async fn create(mut req: Request) -> impl Responder {
    let _a = req.accept();
    let user = req.parse_json::<User>().await?;
    let _payload = req.payload().await?;
    Ok::<_, Error>((200, Json(user)))
}

// http://127.0.0.1:7878/basic/update
// Context-Type : application/x-www-form-urlencoded
pub async fn update(mut req: Request) -> impl Responder {
    // let user = req.parse_form::<User>().await?;
    let user = req.parse_body::<User>().await?;
    Ok::<_, Error>((200, Form(user)))
}

// http://127.0.0.1:7878/basic/multipart_form
// Context-Type : multipart/form-data Form Fields
pub async fn multipart_form(mut req: Request) -> impl Responder {
    // let user = req.parse_form::<User>().await?;
    let user = req.parse_body::<User>().await?;
    // Ok::<_, Error>((200, Form(user)))
    let u = req.parse_body::<User>().await?;
    println!("user = {:#?}",u);
    Ok::<_, Error>((200, Json(user)))
}

// http://127.0.0.1:7878/basic/body_bytes
pub async fn body_bytes(mut req: Request) -> impl Responder {
    let bytes = req.payload().await?;
    let bytes = bytes.clone();
    Ok::<_, Error>((200, bytes))
}
#[allow(dead_code)]
#[derive(Debug)]
struct AppState {
    pub state: u16,
}
#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt().init();
    let mut root = Router::new("basic");
    root.hook(State::new(AppState{state: 11}), None, None);
    root.get("/header", header);
    root.delete("/:email/:age", delete);
    root.get("/query", query);
    root.post("/create", create);
    root.put("/update", update);
    root.post("/multipart_form", multipart_form);
    root.post("/body_bytes", body_bytes);

    info!("root = {:#?}", root);
    let listener = hypers::TcpListener::bind("127.0.0.1:7878").await?;
    hypers::listen(root, listener).await
}

#[cfg(test)]
mod test {
    use feignhttp::{get,delete,post,put,Result};
    use super::{Serialize,Deserialize,User};

    #[derive(Serialize, Deserialize,Debug,PartialEq)]
    pub struct HeaderParams {
        pub host: String,
        pub user_agent: String,
        pub accept: String,
    }
    const BASE_URL: &str = "http://127.0.0.1:7878/basic";

    #[get(url = BASE_URL,path = "/header")]
    async fn header(#[header] head: &HeaderParams) -> Result<HeaderParams>{}

    #[delete(url = BASE_URL,path = "/{email}/{age}")]
    async fn delete(#[path] email: &str,#[path] age: u16) -> Result<User>{}

    #[get(url = BASE_URL,path = "/query")]
    async fn query(#[query] user: User) -> Result<User>{}

    #[post(url = BASE_URL,path = "/create")]
    async fn create(#[body] user: User) -> Result<User>{}

    #[put(url = BASE_URL,path = "/update")]
    async fn update(#[form] email: &str,#[form] age: u16) -> Result<String>{}

    #[post(url = BASE_URL,path = "/multipart_form")]
    async fn multipart_form(#[form] user: User) -> Result<String>{}

    #[post(url = BASE_URL,path = "/body_bytes")]
    async fn body_bytes(#[body] data: Vec<u8>) -> Result<Vec<u8>>{}

    #[tokio::test]
    async fn test() -> Result<()>{
        let header_params = HeaderParams { 
            host: "127.0.0.1:7878".to_owned(), 
            user_agent: "Feign HTTP".to_owned(), 
            accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7".to_owned()
        };
        assert_eq!(header(&header_params).await?,header_params);
        assert_eq!(delete("admin@outlook.com",18).await?,User{ email: Some("admin@outlook.com".to_owned()), age: Some(18) });
        assert_eq!(query(User{ email: Some("admin@outlook.com".to_owned()), age: Some(20) }).await?,User{ email: Some("admin@outlook.com".to_owned()), age: Some(20) });
        assert_eq!(create(User{ email: Some("admin@outlook.com".to_owned()), age: Some(21) }).await?,User{ email: Some("admin@outlook.com".to_owned()), age: Some(21) });
        assert_eq!(update("admin@outlook.com",22).await?,"email=admin%40outlook.com&age=22");
        assert_eq!(multipart_form(User{ email: Some("admin@outlook.com".to_owned()), age: Some(23) }).await?,"email=admin%40outlook.com&age=23");
        assert_eq!(body_bytes(vec![1,2,3]).await?,vec![1,2,3]);
        Ok(())
    }

    #[tokio::test]
    async fn test_() -> Result<()>{
        Ok(())
    }
}
/*
cargo publish -p hypers_macro && cargo publish -p hypers_core && cargo publish -p hypers_openapi_macro && cargo publish -p hypers_openapi && cargo publish -p hypers_session && carg
o publish -p hypers_compression & cargo publish -p hypers
 */